#include "Parser.h"
#include "NewIO.h"
#include <iostream>
#include <sstream>
#include <math.h>

#include "ComponentsHolder.h"
//#include "PlacementReadChanger.h"

using namespace std;

Parser::Parser()
{
	_maxTime = 82800;
	_currentRound = 0;
	_currentFile = 0;

	_dataFile.open(SimulationParameters::IOFileName + SimulationParameters::dataFiles[_currentFile]);	
	_stopTime=0.0;
	_filesTime = 0;

	ofstream fread(SimulationParameters::OutputDirectory+"ReadHistogram.csv");
	ofstream fwrite(SimulationParameters::OutputDirectory+"WriteHistogram.csv");
	fread<<",Group 1,Group2,Group3,total"<<endl;
	fwrite<<",Group 1,Group2,Group3,total"<<endl;
	fread.close();
	fwrite.close();
}

Parser::~Parser(void)
{
	_dataFile.close();
}

bool Parser::readIOs(){
	int read[]={0, 0, 0}, write[]={0, 0, 0};
	int ioCount = 0;
	if (!_dataFile.is_open())
	{
		cout<<"stream not open 1"<<endl;
		return false;
	}
	int roundTime = _currentRound*_maxTime + _filesTime;
	_stopTime+=SimulationParameters::SimulationTime;
	vector<string> parts;
	bool invalidType = false;
	ComponentsHolder* holder = ComponentsHolder::getInstance();
	EventsHandler* handler = ComponentsHolder::getInstance()->getEventsHandler();
	if (_line.size()>0){
		ioCount++;
		parts=split(_line, ' ');
		IOType type;
		if (parts.at(1).compare("Read")==0){
			type= IOType(0);
		}
		else if (parts.at(1).compare("Write")==0){
			type= IOType(1);
		}
		else
		{
			invalidType = true;
		}
		if (!invalidType)
		{
			// add round time to the real time.
			double time = roundTime + atof(parts.at(0).c_str());
			if (time>0){
//				int group=ComponentsHolder::getInstance()->getStatisticsHandler()->getPriorityGroup(hexaToInt(parts.at(3)));
//				if (type==Read)
//					read[group-1]++;
//				else if (type==Write)
//					write[group-1]++;
				ExtentIO* current = new ExtentIO (time, type, hexaToInt(parts.at(3)), atol(parts.at(4).c_str()), atoi(parts.at(5).c_str()));
				Event* e = new NewIO(time, current);
				handler->addEvent(e);
			}
		}
	}
	bool found = false;
	while (_dataFile.good()){

		found = true;
		getline(_dataFile, _line);
		if (_line.size() == 0)
			continue;
		parts=split(_line, ' ');
		if (parts.size() != 6)
			continue;

		double dataTime = atof (parts.at(0).c_str());
		if (dataTime < 0 ) 
			continue;
		// add round time to the real time.
		double time = roundTime + dataTime;
		if (time<= _stopTime && time >= (_stopTime - SimulationParameters::SimulationTime)){
			IOType type=IOType(0);
			if (parts.at(1).compare("Read")==0){
				type= IOType(0);
			}
			else if (parts.at(1).compare("Write")==0){
				type= IOType(1);
			}
			else
				continue;
			if (time<0)
				continue;
//			int group=ComponentsHolder::getInstance()->getStatisticsHandler()->getPriorityGroup(hexaToInt(parts.at(3)));
//			if (type==Read)
//				read[group-1]++;
//			else if (type==Write)
//				write[group-1]++;
			ExtentIO* current = new ExtentIO (time, type, hexaToInt(parts.at(3)), atol(parts.at(4).c_str()), atoi(parts.at(5).c_str()));
			if (current->getType() != Read && current->getType() != Write)
				throw "Invalid type!!!";
			// TODO: remove ioCount*Microsec
			Event* e = new NewIO(time, current);
			handler->addEvent(e);
			ioCount++;
		}
		else{
			//cout << "Num of ios is: " << ioCount <<endl;
			ofstream fread(SimulationParameters::OutputDirectory+"ReadHistogram.csv", ios::app);
			ofstream fwrite(SimulationParameters::OutputDirectory+"WriteHistogram.csv", ios::app);
			fread<<_stopTime<<","<<read[0]<<","<<read[1]<<","<<read[2]<<","<<(read[0]+read[1]+read[2])<<endl;
			fwrite<<_stopTime<<","<<write[0]<<","<<write[1]<<","<<write[2]<<","<<(write[0]+write[1]+write[2])<<endl;
			fread.close();
			fwrite.close();
			for (int i=0; i<3; i++){
				read[i]=0;
				write[i]=0;
			}
			return true;	
		}
	}
	if (found)
	{
		ofstream fread(SimulationParameters::OutputDirectory+"ReadHistogram.csv", ios::app);
		ofstream fwrite(SimulationParameters::OutputDirectory+"WriteHistogram.csv", ios::app);
		fread<<_stopTime<<","<<read[0]<<","<<read[1]<<","<<read[2]<<","<<(read[0]+read[1]+read[2])<<endl;
		fwrite<<_stopTime<<","<<write[0]<<","<<write[1]<<","<<write[2]<<","<<(write[0]+write[1]+write[2])<<endl;
		fread.close();
		fwrite.close();
		for (int i=0; i<3; i++){
			read[i]=0;
			write[i]=0;
		}
		if (SimulationParameters::NumberOfRounds == 1)
			_maxTime = _stopTime;
	}
	else
	{
		cout << "finished read!!!\n";
		cout << "Finished exceution " << (_currentRound+1) << "/" << SimulationParameters::NumberOfRounds << " of file: " << SimulationParameters::dataFiles[_currentFile] << endl;
		if (_currentRound < SimulationParameters::NumberOfRounds - 1)
		{
			_stopTime-=SimulationParameters::SimulationTime;
			if (_currentRound == 0)
				_maxTime = _stopTime;
			_currentRound++;
			_dataFile.close();
			_dataFile.open(SimulationParameters::IOFileName + SimulationParameters::dataFiles[_currentFile]);
			return readIOs();
		}
		if (_currentFile < SimulationParameters::dataFiles.size() - 1)
		{
			_stopTime-=SimulationParameters::SimulationTime;
			_filesTime += (_currentRound+1)*_maxTime;
			_currentFile++;
			_currentRound = 0;
			_dataFile.close();
			cout << "Switch file to: " << SimulationParameters::IOFileName + SimulationParameters::dataFiles[_currentFile] << endl;
			_dataFile.open(SimulationParameters::IOFileName + SimulationParameters::dataFiles[_currentFile]);
			return readIOs();
		}
	}
	cout << "Num of ios is: " << ioCount <<endl;
	_line = "";
	return found;
}

/*
bool Parser::readPlacements(map <string , pair<Device*, vector<int>*>>* placements, map <string, string>* newPlacements)
{
	if (_stopTime == SimulationParameters::SimulationTime)
		return true;	
	cout << "Stop time is: " << _stopTime << endl;
	bool found = false;
	map<string,string>::iterator it = newPlacements->begin();
	Storage* storage = ComponentsHolder::getInstance()->getStorage();
	double time = ComponentsHolder::getInstance()->getEventsHandler()->getTime();
	int counter=0;
	for (; it!=newPlacements->end(); it++){
		
		string lunid;
		long offset;
		string lunOffset = it->first;
		stringstream s1(lunOffset);
		s1 >> lunid >> offset;
		stringstream s;
		s << lunid << "," << offset;

		double i = (rand() % (SimulationParameters::placementDistribution*4))/4.0;
		i += time;
		map <string , pair<Device*, vector<int>*>>::iterator oldIter;
		oldIter = placements->find(s.str());
		if (oldIter==placements->end())
		{
			if (it->second != "SATA")
			{
				Event* e = new PlacementReadChanger(i, s.str(), "SATA", it->second);
				ComponentsHolder::getInstance()->getEventsHandler()->addEvent(e);
				if (it->second== "SSD")
					counter++;
			}
		}
		else if (it->second!=oldIter->second.first->_type){
			Event* e = new PlacementReadChanger(i, s.str(), oldIter->second.first->_type, it->second);
			ComponentsHolder::getInstance()->getEventsHandler()->addEvent(e);
			
			if (it->second == "SSD" || oldIter->second.first->_type == "SSD")
				counter++;
		}

		found = true;
	}
	cout<<"counter: "<<counter<<endl;

	return found;
}
*/
/*
bool Parser::readPlacements(map <string , pair<Device*, vector<int>*>>* placements)
{ 

// first time slice we don't read placements
if (_stopTime == SimulationParameters::SimulationTime)
return true;
ifstream placementFile;
placementFile.open(SimulationParameters::ExtentsFileName);
cout << "Stop time is: " << _stopTime << endl;

if (!placementFile.is_open())
{
cout<<"placementFile not open!!!"<<endl;
throw "placementFile not open!!!";
}

vector<string> parts;
bool found = false;
string placementline;
int currentTime = _stopTime - SimulationParameters::SimulationTime;
while (placementFile.good())
{
//placementFile >> placementline;
getline(placementFile, placementline);
// empty line
if (placementline.size() == 0)
continue;
parts=split(placementline, '\t');

int time = atoi(parts.at(0).c_str());

// all the time should be equla to current time
if (time != currentTime)
{
cout << "Wrong time in read placement\ntime: "<<time<<" currentTime: "<<currentTime<<endl;
throw "Wrong time in read placement\n";
}
long offset = atoi(parts[3].c_str());
int align = EXTENT_SIZE/KB;
long alignedOffset = (long)(floor( ((double)offset) / (align))*align);
stringstream s;
s << parts[2] << "," << alignedOffset;
Device* device = ComponentsHolder::getInstance()->getDevice(parts[1]);
if ((placements->find(s.str())==placements->end()))
placements->insert(pair<string, pair<Device*, vector<int>*>>(s.str(), pair<Device*, vector<int>*>(device, device->getRandomDevice())));
found = true;
}
std::cout << "Number of placements are: " << placements->size() << endl;
placementFile.close();
return found;

return false;
}
*/
vector <string> Parser::split (string str, char delimiter)
{
	vector<string> ans;
	stringstream ss (str);
	int length = str.length();
	char *a=new char[str.size()+1];
	while (ss.good() )
	{
		ss.getline(a, length, delimiter);
		if (strlen(a)==0)
			continue;
		ans.push_back(string(a));
	}
	delete [] a;
	return ans;
}

int Parser::hexaToInt(string shex){
	int ans;
	stringstream hex(shex);
	hex >> std::hex >> ans;
	return ans;
}
/*
void Parser::readPriorityGroups(){
	ofstream f(SimulationParameters::OutputDirectory+"groups.txt");
	char buf[1001];
	ifstream priority (SimulationParameters::PriorityFile);
	map<int, int> priorityGroups;
	while (priority.good()){
		priority.getline(buf,100);
		string str = string(buf);
		istringstream istr (str);
		string pline;

		istr >> pline;
		int lunID = atoi(pline.c_str());

		istr >> pline;
		int group = atoi(pline.c_str());

		priorityGroups.insert(pair<int, int>(lunID, group));
		f<<lunID<<"\t"<<group<<endl;
	}
	ComponentsHolder::getInstance()->getStatisticsHandler()->setPriorityGroups(priorityGroups);
	f.close();
}
*/
